#!/usr/bin/python3
# -*- coding: utf-8 -*-

from os.path import isdir, abspath, normpath, join as joinpath
from flask import Flask, render_template

from .core.job_runner import init_runner_pool

APP_NAME = 'ikanda'
APP_VERSION = '0.1 Beta'


def create_app(
        instance_path: str = None,
        show_sql: bool = False,
        **kwargs
) -> (Flask, None):
    # create app
    app = Flask(__name__, instance_path=instance_path, instance_relative_config=True)
    app.extensions['APP_NAME'] = APP_NAME
    app.extensions['APP_VERSION'] = APP_VERSION

    # configure logging
    _initialize_logging(app, show_sql=show_sql)

    # configure app with default settings
    app.config.from_mapping(
        SECRET_KEY='dev',
        SQLALCHEMY_TRACK_MODIFICATIONS=False,
        SQLALCHEMY_ECHO=False,
        INDEX_TEMPLATE='index.html'
    )

    # configure app with instance_path/<app>.json
    # check the instance folder exists
    if not isdir(app.instance_path):
        app.logger.critical('Instance path NOT FOUND: %s', app.instance_path)
        return None
    else:
        app.logger.info('Instance path found: %s', app.instance_path)

    # load the instance config, if it exists, when not testing
    app.logger.info('Loading configuration from ikanda.json')
    if app.config.from_json('ikanda.json', silent=True):
        app.logger.info('Configuration loaded')
    else:
        app.logger.warning('No configuration found, use default configuration')

    # load the important configuration if passed in
    app.config.from_mapping(kwargs)

    # configure database
    database = app.config.get('DATABASE', None)
    if database:
        database = abspath(normpath(joinpath(app.instance_path, database)))
        app.logger.info('Use database: %s', database)
        app.config.update(SQLALCHEMY_DATABASE_URI='sqlite:///' + database)
    else:
        app.logger.info('Use memory database')
        app.config.update(SQLALCHEMY_DATABASE_URI='sqlite:///:memory:')

    # configure jinja2
    app.jinja_env.trim_blocks = True
    app.jinja_env.lstrip_blocks = True

    # configure projects, nodes and jobs
    app.logger.info('Loading metadata')
    from .core.metadata import load_all
    if load_all(
            joinpath(app.config.root_path, 'credentials.yaml'),
            joinpath(app.config.root_path, 'jobs.yaml')
    ):
        app.logger.info('Meta data loaded')
    else:
        app.logger.critical('No meta data loaded')

    # register with app
    _register_extensions(app)
    _register_blueprints(app)
    _register_template_contexts(app)
    _register_commands(app)

    # initialize runner pool
    init_runner_pool()

    # home
    @app.route('/')
    @app.route('/index')
    def home():
        return render_template(app.config['INDEX_TEMPLATE'])

    return app


def _initialize_logging(
        app: Flask,
        show_sql: bool
) -> None:
    """ 初始化日志记录器。

    :param app: app对象。
    """
    from sys import stdout
    from logging import basicConfig, getLogger, StreamHandler, INFO, DEBUG
    from logging.handlers import TimedRotatingFileHandler

    format = '%(asctime)s [%(levelname)-7s] %(name)s (%(pathname)s:%(lineno)d) - %(message)s'
    datefmt = '%Y-%m-%d %H:%M:%S'

    if not app.debug:
        # 非debug模式下，使用文件日志。
        basicConfig(
            format=format, datefmt=datefmt,
            handlers=(TimedRotatingFileHandler(filename='logs/' + app.name + '.log', interval=6),),
            level=INFO
        )
    else:
        # 否则使用控制台日志。
        basicConfig(
            format=format, datefmt=datefmt,
            handlers=(StreamHandler(stream=stdout),),
            level=DEBUG
        )

    if show_sql:
        # 激活sqlalchemy的日志。
        getLogger('sqlalchemy.engine.base.Engine').setLevel(INFO)


def _register_extensions(
        app: Flask
) -> None:
    """ 注册各种扩展。

    :param app: 用于注册扩展的app对象。
    """
    # configure app with bootstrap
    from flask_bootstrap import Bootstrap
    Bootstrap().init_app(app)

    # configure app with moment
    from flask_moment import Moment
    Moment().init_app(app)

    from .views import Authorization
    Authorization().init_app(app)

    # configure app with db and migrate
    from .models import db, migrate
    db.init_app(app)
    migrate.init_app(app, db)


def _register_blueprints(
        app: Flask
) -> None:
    """注册蓝本。

    :param app: 用于注册蓝本的app对象。
    """
    from ikanda.views import projects, nodes, jobs
    app.register_blueprint(projects, url_prefix='/projects')
    app.register_blueprint(nodes, url_prefix='/nodes')
    app.register_blueprint(jobs, url_prefix='/jobs')


def _register_template_contexts(
        app: Flask
) -> None:
    """注册模版上下文。

    :param app: 用于注册模板上下文的app对象。
    """
    # 加入页面时间戳。`
    # app.context_processor(lambda: {'timestamp': now().datetime})


def _register_commands(
        app: Flask
):
    """ 注册自定义命令。

    :param app: 用于注册自定义命令的app对象。
    """
    from .db_utils import init_db
    app.cli.add_command(init_db)
